package com.yc.xunlei;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
public class Xunlei {
    private static Logger log = Logger.getLogger(Xunlei.class.getName());

    private DownLoadedSizeNotify dlsn;

    private static  volatile long fileSizeDownLoaded= 0;//修饰属性  long 不能保证原子性  文件下载总大小不对了
//    给Xunlei.class 加锁  synchronized  性能很差
    //private static AtomicLong fileSizeDownLoaded  =new AtomicLong(0L); //保证原子性
    public static void main(String[] args) throws IOException, InterruptedException {
        List<Thread> list =new ArrayList<>();
        long start =System.currentTimeMillis();


        String url = "https://softdl6.360tpcdn.com/pcrj/setup%E9%92%89%E9%92%89%E4%B8%AA%E4%BA%BA%E7%89%88_2000000180_4015547.exe";
        //1.获取要下载的文件大小
        long fileSize= getDownLoadFileSize(url);
        log.info("待下载的文件大小："+fileSize);
        //2.新文件的保存位置
        String newFileName = genFileName(url);
        log.info("下载后新文件名为："+newFileName);
        String userhome = System.getProperty("user.home"); //新文件的路径
        String downloadPath =userhome+ File.separator + newFileName;
        log.info("下载后新文件的路径："+downloadPath);
        //3.创建新文件
        RandomAccessFile raf = new RandomAccessFile(downloadPath, "rw");//“rw”:模式：读写模式
        raf.setLength(fileSize);
        raf.close();
        log.info("空的文件夹创建成功");
        //4.获取本机的核数-》根据核数分线程数
        long threadSize =Runtime.getRuntime().availableProcessors();
        log.info("线程数："+threadSize); //16个
        //5.计算每个线程下载的字节数
        long sizePerThread =getSizePerThread(fileSize,threadSize);
        log.info("每个线程要下载的数据量为："+sizePerThread);
        //6.循环创建线程
        //创建通知多个对象，与每个任务绑定，即通知对象有多个，多个线程有多个，多个线程都累加和到fileSizeDownLoaded 属性上   -> 这是一种线程资源争抢
        for (int i=0;i<threadSize;i++) {

            DownLoadedSizeNotify dlsn = new DownLoadedSizeNotify() {
                @Override
                public void notifySize(long size) {
//                    //fileSizeDownLoaded+ =size;//不能保证原子性
                    //方案二：加锁  //程序运行的总时长：1012
                     synchronized (Xunlei.class) {
                         fileSizeDownLoaded += size;
                         System.out.println("已下载的文件总大小："+fileSizeDownLoaded); //994 536 字节

                     }
                    //方案三：利用原子类型   //程序运行的总时长：1460
//                    fileSizeDownLoaded.addAndGet(size);
                    //System.out.println("已下载的文件总大小："+fileSizeDownLoaded.get()); //994 536 字节
                }
            };
                                        // 第几个线程  文件总大小 线程数  每个线程下载的数据量  url下载地址  下载后的保存路径
            DownLoadTask  task =new DownLoadTask(i,fileSize,threadSize,sizePerThread,url,downloadPath,dlsn);
            Thread thread =new Thread(task);
            thread.start();
            list.add(thread);
        }

        for (Thread t:list) {
            t.join();
        }
        System.out.println("主线程阻塞在此，等待子线程执行完毕");
        long end =System.currentTimeMillis();
        System.out.println("程序运行的总时长："+(end-start));
    }

    /**
     * 计算每个线程要下载的字节数
     * @param fileSize
     * @param threadSize
     * @return
     */
    private static long getSizePerThread(long fileSize, long threadSize) {
        return fileSize%threadSize ==0?fileSize/threadSize :fileSize/threadSize+1;
    }

    /**
     * 根据日期时间生成新文件的文件名
     * @param url
     * @return
     */
    private static String genFileName(String url) {
        Date d=new Date();
        DateFormat df =new SimpleDateFormat("yyyyMMddHHmmssSS");
        String prefix = df.format(d);
        //后缀名
        String suffix = url.substring(url.lastIndexOf("."));
        return prefix+suffix;
    }

    /**
     * 下载前获取文件的大小
     * @param url
     * @return
     */
    private static long getDownLoadFileSize(String url) throws IOException {
        long filesize =0;
        URL u =new URL(url); //u是一个URL对象，它包含了要访问的网址信息；
        HttpURLConnection connection = (HttpURLConnection) u.openConnection();
        //设置请求头
        connection.setRequestMethod("HEAD");////head不下载文件内容，保儿取晌应头域(文件大小 )
        //设置超时 时间
        connection.setConnectTimeout(300);
        filesize =connection.getContentLength();
        log.info("文件大小");
        return filesize;
    }
}

/**
 * 下载数据量的回调接口
 */
interface DownLoadedSizeNotify{
    public void notifySize(long size);
}
